Skip to content

feat: 支持按模型配置 token 价格,聚合 API 模型同步时自动创建零价格规则#279

Open
panzeyu2013 wants to merge 1 commit into
qxcnm:mainfrom
panzeyu2013:feature/model-price-rules-config
Open

feat: 支持按模型配置 token 价格,聚合 API 模型同步时自动创建零价格规则#279
panzeyu2013 wants to merge 1 commit into
qxcnm:mainfrom
panzeyu2013:feature/model-price-rules-config

Conversation

@panzeyu2013
Copy link
Copy Markdown
Contributor

变更摘要

  • 问题:聚合 API(如 DeepSeek、Mistral)的模型同步后缺少 model_price_rules 记录,导致定价引擎匹配失败,estimated_cost_usd 始终为 null(前端显示 price_status: "missing"),钱包不扣费,成本统计无法正常工作。

  • 根因auto_associate_source_modelscrates/service/src/apikey/apikey_models.rs:821)在同步聚合 API 模型时只创建了 platform model 目录条目和 model_source_mapping 路由映射,没有创建价格规则。定价引擎 estimate_cost_usd_for_log 先查 DB rules → 未命中 → fallback 硬编码 PRICE_SEEDS → 也未命中 → 返回 None → 0.0

  • 方案:同步时自动为聚合 API 模型创建价格规则(价格均为 0),并支持用户在模型编辑弹窗中手动设置价格。用户规则 priority=20000,确定性 ID=user-{slug},确保覆盖所有自动创建和官方 seed 规则。


改动范围

  • Frontend
  • Desktop / Tauri
  • Service
  • Gateway / Protocol Adapter
  • Docs / Governance
  • Workflow / Release

主要文件

Rust 后端(4 个文件)

  • crates/core/src/rpc/types.rs

    • 新增 ModelPriceRuleEntry
    • 新增 ModelPriceRuleListResult
    • 新增 ModelPriceRuleUpsertInput
  • crates/service/src/quota/read.rs

    • 新增 list_model_price_rules
    • 新增 read_model_price_rule
    • 新增 upsert_model_price_rule
    • 新增 price_rule_entry 辅助函数
  • crates/service/src/rpc_dispatch/quota.rs

    • 新增 quota/modelPriceRules/list
    • 新增 quota/modelPriceRule/read
    • 新增 quota/modelPriceRule/upsert
    • 三个 RPC 路由
  • crates/service/src/apikey/apikey_models.rs

    • 新增 ensure_model_price_rules_for_aggregate_api
    • 在聚合 API 模型同步时自动创建零价格规则
    • 通过 existing_patterns HashSet 先查后插
    • 已有任意规则(含用户编辑、官方 seed、其他聚合 API 同步的)则跳过,确保不覆盖
    • 修复了原有 for source_model in source_models 的 borrow-after-move 编译错误

前端(5 个文件)

  • apps/src/lib/api/account-client.ts

    • 新增 ModelPriceRuleEntry
    • 新增 ModelPriceRuleUpsertPayload
    • 新增 listModelPriceRules
    • 新增 readModelPriceRule
    • 新增 upsertModelPriceRule
  • apps/src/lib/api/transport-web-commands.ts

    • 新增 service_model_price_rules_list
    • 新增 service_model_price_rule_read
    • 新增 service_model_price_rule_upsert
    • 三个 web 命令映射
  • apps/src/components/modals/model-catalog-modal.tsx

    • 在“保留本地覆写”开关和“高级 JSON”之间新增 3 个价格输入框
      • 输入价格
      • 缓存输入价格
      • 输出价格
    • 单位:USD / 1M tokens
    • 保存模型时通过 onSavePriceRule 回调同步写入规则
  • apps/src/hooks/useManagedModels.ts

    • 新增 saveModelPriceRule hook 方法
  • apps/src/app/models/page.tsx

    • 解构并传递 onSavePriceRule

桌面端 Tauri(2 个文件)

  • apps/src-tauri/src/commands/apikey.rs

    • 新增 service_model_price_rules_list
    • 新增 service_model_price_rule_read
    • 新增 service_model_price_rule_upsert
    • 三个 #[tauri::command]
  • apps/src-tauri/src/commands/registry.rs

    • invoke_handler! 宏中注册上述三个命令

验证

  • pnpm -C apps run test
  • pnpm -C apps run build
  • pnpm -C apps run test:ui
  • cargo test --workspace
  • 其他本地验证已说明

已执行的实际验证:

Rust 编译检查

cargo check --workspace

结果:0 errors, 0 warnings (0.82s)

Rust 全部测试

cargo test --workspace

结果:1122 tests passed, 0 failed, 0 ignored

前端构建

pnpm -C apps run build

结果:12 static pages, 0 errors

桌面端构建(含 Tauri)

pnpm -C apps run build:desktop

结果:12 static pages, Tauri commands registered, 0 errors


风险与影响面

直接影响

  • 定价引擎:

    • 无改动(model_pricing.rs 不变)
    • 已有 DB rules 优先 + seeds fallback 逻辑自动匹配新增规则
  • 请求日志:

    • 无改动(request_log.rsaggregate_api.rsproxy.rs 等均不变)
  • OpenAI account 同步:

    • 无影响(仅 source_kind == "aggregate_api" 触发)
  • 官方模型定价:

    • 无影响
    • 用户规则 priority=20000
    • 自动创建 priority=-10
    • 官方 seed priority≈9999
    • 排序关系:20000 > 9999 > -10
    • 用户规则优先,无用户规则时 seed 仍生效

边界注意

  • 修复前:

    • 聚合 API 模型 cost=0
    • 钱包不扣费
    • 用户零感知
  • 修复后:

    • 价格默认为 0
    • cost 仍为 0
    • 钱包仍不扣费(base_cost_usd <= 0.0 跳过)
    • 用户必须手动设价后才会产生实际扣费
  • 重同步保护:

    • existing_patterns 包含所有已启用规则的 model_pattern
    • 已有规则一律 skip
    • 不会覆盖用户手动设置的价格
  • 编辑已有模型:

    • 当前版本打开编辑弹窗时价格字段为空(已知限制)
    • 后续迭代可增加价格回填
    • 保存时只发送有值的字段

备注

  • 自动创建的 rule 标识:

    • source: "aggregate_api_sync"
    • priority=-10
  • 官方 seed:

    • source: "official_seed"
  • 用户规则:

    • source: "custom"
    • priority=20000
  • 分层关系清晰

  • 用户 upsert 使用 ID:

    • user-{model_pattern}
  • 特性:

    • 确定性
    • 可重入
    • 不创建重复规则

…egate API models

- Auto-create model_price_rules (price=0) when syncing aggregate API models
- Only insert on first sync, never overwrite existing user-set prices
- Add RPC endpoints: quota/modelPriceRules/list, read, upsert
- Add price input fields (input/cached/output per 1M tokens) to model edit modal
- Register Tauri commands for desktop support
- User-created rules use priority=20000,ID=user-{slug} to override official seeds
Copy link
Copy Markdown
Collaborator

@KilimiaoSix KilimiaoSix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes,暂不建议合并。

这次改动方向是合理的,但当前实现里有几个会影响功能正确性的点,需要先修复:

  1. model-catalog-modal.tsx 里的价格字段打开弹窗时始终是空值,编辑已有模型时不会读取已有价格规则。用户只改一个价格字段时,其他未填字段会被保存成 null,而后端计价逻辑要求 input/output 价格都存在,可能导致整条自定义价格规则失效。建议保存前先读取并预填现有规则,更新时做 merge,而不是把空字段覆盖为 null

  2. 聚合 API 自动创建的零价格规则优先级是 -10,但官方价格 seed 的优先级是 10000 左右,实际匹配会优先命中官方规则。因此普通 OpenAI 模型名不会按自动创建的零价格规则计费,这和 PR 描述不一致。建议明确自动零价规则的优先级策略,或者只在没有更高优先级用户规则时生成能够实际生效的规则。

  3. 价格规则保存失败目前被前端吞掉了,弹窗仍然关闭,用户会误以为模型和价格都保存成功。建议价格保存失败时展示错误并阻止关闭,或者把模型保存和价格保存设计成清晰的两阶段状态。

另外,当前 diff 还有 trailing whitespace,git diff --check 会失败,需要清理。

验证情况:

  • cargo check -p codexmanager-service 通过
  • cargo test -p codexmanager-service aggregate_ 通过
  • git diff --check 158adf8...HEAD 失败,存在 trailing whitespace
  • pnpm -C apps run build 未能执行,临时 worktree 缺少 apps/node_modules,找不到 next

@panzeyu2013
Copy link
Copy Markdown
Contributor Author

合理的,我再重点排查一下和目前定价相关的逻辑

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants